﻿/* Open source distributed without warranties of any kind. */

namespace Transcriber.Plugins
{
    #region Using
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Xml.Serialization;
    #endregion

    /// <summary>
    /// Root element of a transcriber rules file.
    /// </summary>
    [XmlRoot("rules", Namespace = "http://transcriber.codeplex.com/Rules.xsd")]
    public sealed class Rules
    {
        private List<RulesElement> mRules = new List<RulesElement>();
        private bool mCaseSensitive;
        private string mTitle = string.Empty;
        private string mCopyright = string.Empty;
        private string mAuthor = string.Empty;
        private UnmatchedAction mUnmatchedAction = UnmatchedAction.Omit;
        private string mReplacementText = "\u00A4";
        private string mLoadedFrom;
        private bool mDisplayInUserInterface = true;

        /// <summary>
        /// Gets or sets a value indicating whether or not to display the rules file in the user interface.
        /// </summary>
        /// <value>
        /// A value indicating whether or not to display the rules file in the user interface.
        /// </value>
        [XmlAttribute("displayInUserInterface")]
        public bool DisplayInUserInterface
        {
            get { return mDisplayInUserInterface; }
            set { mDisplayInUserInterface = value; }
        }

        /// <summary>
        /// Gets the rules directory.
        /// </summary>
        /// <value>The rules directory.</value>
        public static DirectoryInfo RulesDirectory
        {
            get
            {
                return new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Rules"));
            }
        }

        /// <summary>
        /// Gets or sets the pathname from which this <see cref="Rules"/> instance was loaded.
        /// </summary>
        /// <value>The pathname from which this <see cref="Rules"/> instance was loaded.</value>
        [XmlIgnore]
        public string LoadedFrom
        {
            get { return this.mLoadedFrom; }
            set { this.mLoadedFrom = value; }
        }

        /// <summary>
        /// Gets or sets the replacement text.
        /// </summary>
        /// <value>The replacement text.</value>
        [XmlAttribute("replacementText")]
        public string ReplacementText
        {
            get { return mReplacementText; }
            set { mReplacementText = value; }
        }

        /// <summary>
        /// Gets or sets the unmatched action.
        /// </summary>
        /// <value>The unmatched action.</value>
        [XmlAttribute("unmatchedAction")]
        public UnmatchedAction UnmatchedAction
        {
            get { return mUnmatchedAction; }
            set { mUnmatchedAction = value; }
        }

        /// <summary>
        /// Gets or sets the author.
        /// </summary>
        /// <value>The author.</value>
        [XmlAttribute("author")]
        public string Author
        {
            get { return mAuthor; }
            set { mAuthor = value; }
        }

        /// <summary>
        /// Gets or sets the title.
        /// </summary>
        /// <value>The title.</value>
        [XmlAttribute("title")]
        public string Title
        {
            get { return mTitle; }
            set { mTitle = value; }
        }

        /// <summary>
        /// Gets or sets the copyright notice.
        /// </summary>
        /// <value>The copyright notice.</value>
        [XmlAttribute("copyright")]
        public string Copyright
        {
            get { return mCopyright; }
            set { mCopyright = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is case sensitive.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is case sensitive; otherwise, <c>false</c>.
        /// </value>
        [XmlAttribute("caseSensitive")]
        public bool CaseSensitive
        {
            get { return this.mCaseSensitive; }
            set { this.mCaseSensitive = value; }
        }

        /// <summary>
        /// Gets the list of rules.
        /// </summary>
        /// <value>The rules.</value>
        [XmlElement("rule", typeof(Rule))]
        [XmlElement("import", typeof(Import))]
        public List<RulesElement> Items
        {
            get { return this.mRules; }
        }

        /// <summary>
        /// Loads the <see cref="Rules"/> in the specified file.
        /// </summary>
        /// <param name="file">The file.</param>
        /// <returns>The <see cref="Rules"/> in the specified file.</returns>
        public static Rules Load(FileInfo file)
        {
            using (Stream stream = file.OpenRead())
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Rules));
                Rules rules = (Rules)serializer.Deserialize(stream);
                rules.LoadedFrom = file.Name;
                Rules.LoadImportedRules(rules);
                return rules;
            }
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return this.mTitle;
        }

        private static void LoadImportedRules(Rules rules)
        {
            List<RulesElement> items = new List<RulesElement>();

            foreach (RulesElement rulesElement in rules.Items)
            {
                Rule rule = rulesElement as Rule;

                if (null != rule)
                {
                    Rules.AddRulesElement(rule, items);
                    continue;
                }

                Import import = rulesElement as Import;

                if (null != import)
                {
                    Rules.AddRulesElement(import, items);
                    continue;
                }
            }

            rules.Items.Clear();
            rules.Items.AddRange(items);
        }

        private static void AddRulesElement(Rule rule, List<RulesElement> items)
        {
            items.Add(rule);
        }

        private static void AddRulesElement(Import import, List<RulesElement> items)
        {
            FileInfo file = new FileInfo(Path.Combine(Rules.RulesDirectory.FullName, import.Filename));
            Rules importedRules = Rules.Load(file);
            items.AddRange(importedRules.Items);
        }
    }
}